// ES6 ES2015
// promise 状态
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'

function execFunctionWithCatchError(execFn, value, resolve, reject) {
  try {
    const result = execFn(value)
    resolve(result)
  } catch(err) {
    reject(err)
  }
}

class YZPromise {
  constructor(executor) {
    this.status = PROMISE_STATUS_PENDING
    this.value = undefined
    this.reason = undefined
    this.onFulfilledFns = []
    this.onRejectedFns = []

    const resolve = (value) => {
      if(this.status === PROMISE_STATUS_PENDING) {
        queueMicrotask(() => {
          if(this.status !== PROMISE_STATUS_PENDING) return 
          this.status = PROMISE_STATUS_FULFILLED
          this.value = value
          this.onFulfilledFns.forEach(fn => {
            fn(this.value)
          })
        })
        // setTimeout(() => {
        //   this.status = PROMISE_STATUS_FULFILLED
        //   console.log("resolve被调用");
        //   this.value = value
        //   this.onFulfilled()
        // }, 0)
      }
    }
    const reject = (reason) => {
      if(this.status === PROMISE_STATUS_PENDING) {
        queueMicrotask(() => {
          if(this.status !== PROMISE_STATUS_PENDING) return 
          this.status = PROMISE_STATUS_REJECTED
          this.reason = reason
          this.onRejectedFns.forEach(fn => {
            fn(this.reason)
          })
        })
      }
    }
    try {
      executor(resolve, reject)
    } catch(err) {
      reject(err)
    }
  }

  then(onFulfilled, onRejected) {
    onRejected = onRejected || (err => { throw err })
    onFulfilled = onFulfilled || (value => { return value })

    return new YZPromise((resolve, reject) => {
      // 1.如果then调用时，状态已经确定
      if(this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
        // try {
        //   const value = onFulfilled(this.value)
        //   resolve(value)
        // } catch(err) {
        //   reject(err)
        // }
        execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
        
      }
      if(this.status === PROMISE_STATUS_REJECTED && onRejected) {
        execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
      }
      // 2.将成功和失败放入到数组中
      // 将成功、失败的回调放在数组中
      if(this.status === PROMISE_STATUS_PENDING) {
        if(onFulfilled) this.onFulfilledFns.push(() => {
          execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
          
        })
        if(onRejected) this.onRejectedFns.push(() => {
          execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
        })
      }
    })
  }

  catch(onRejected) {
    return this.then(undefined, onRejected)
  }

  finally(onFinally) {
    this.then(() => {
      onFinally()
    }, () => {
      onFinally()
    })
  }

  static resolve(value) {
    return new YZPromise((resolve) => {
      resolve(value)
    })
  }

  static reject(reason) {
    return new YZPromise((resolve, reject) => {
      reject(reason)
    })
  }

  static all(promises) {
    return new YZPromise((resolve, reject) => {
      const values = []
      promises.forEach(promise => {
        promise.then(res => {
          values.push(res)
          if(values.length === promises.length) {
            resolve(values)
          }
        }, err => {
          // 只要有一个reject，直接返回
          reject(err)
        })
      })
    })
  }

  static allSettled(promises) {
    return new YZPromise((resolve) => {
      const results = []
      promises.forEach(promise => {
        promise.then(res => {
          results.push({status: PROMISE_STATUS_FULFILLED, value: res})
          if(results.length === promises.length)
            resolve(results)
        }, err => {
          results.push({status: PROMISE_STATUS_REJECTED, value: err})
          if(results.length === promises.length)
            resolve(results)
        })
      })
    })
  }
}

const p1 = new YZPromise((resolve) => {
  setTimeout(() => {resolve(11)}, 1000)
  
})

const p2 = new YZPromise((resolve, reject) => {
  setTimeout(() => {reject(22)}, 2000)
})

const p3 = new YZPromise((resolve) => {
  setTimeout(() => {resolve(33)}, 3000)
})

YZPromise.allSettled([p1, p2, p3]).then(res => {
  console.log(res);
}).catch(err => {
  console.log(err);
})

